package puzzle.hashikake.solver;

import java.util.ArrayList;
import java.util.List;

import puzzle.hashikake.data.HashiState;
import puzzle.hashikake.data.HashikakeBoard;
import puzzle.hashikake.data.ShimaDef;

class HashikakeTopologyAnalyzer {
	private HashikakeBoard m_board;

	private int[] m_networkNumberForHashi;
	private int[] m_networkNumberForShima;
	private List<Boolean> m_isClosedFlags;

	private int m_networkNumber;
	private boolean m_allShimasSatisfied;

	public HashikakeTopologyAnalyzer(HashikakeBoard b) {
		m_board = b;
	}

	public HashikakeTopology analyze() {
		m_networkNumberForHashi = new int[m_board.getHashiStates().size()];
		m_networkNumberForShima = new int[m_board.getShimaList().size()];
		m_isClosedFlags = new ArrayList<Boolean>();
		m_networkNumber = 0;

		for (ShimaDef shima : m_board.getShimaList()) {
			if (findUnvisitedHashiFrom(shima) != null) {
				m_networkNumber++;
				m_allShimasSatisfied = true;
				traverseFrom(shima);
				m_isClosedFlags.add(m_allShimasSatisfied);
			}
		}

		return new HashikakeTopology(m_networkNumber, m_networkNumberForHashi,
				m_networkNumberForShima, m_isClosedFlags);
	}

	private void traverseFrom(ShimaDef shima) {
		if (getNetworkNumberFor(shima) == 0) {
			m_allShimasSatisfied &= m_board.isShimaSatisfied(shima);
			setNetworkNubmerFor(shima, m_networkNumber);
		} else {
			assert getNetworkNumberFor(shima) == m_networkNumber;
		}

		HashiState s;
		while ((s = findUnvisitedHashiFrom(shima)) != null) {
			setNetworkNubmerFor(s, m_networkNumber);
			traverseFrom(s.def.getOppositeOf(shima));
		}
	}

	private HashiState findUnvisitedHashiFrom(ShimaDef shima) {
		HashiState[] hashiStates = m_board.getHashiStatesForShima(shima);
		for (HashiState s : hashiStates) {
			if (s.getNumber() > 0 && getNetworkNumberFor(s) == 0) {
				return s;
			}
		}
		return null;
	}

	private int getNetworkNumberFor(HashiState s) {
		return m_networkNumberForHashi[s.def.i];
	}

	private void setNetworkNubmerFor(HashiState s, int n) {
		m_networkNumberForHashi[s.def.i] = n;
	}

	private int getNetworkNumberFor(ShimaDef shima) {
		return m_networkNumberForShima[shima.i];
	}

	private void setNetworkNubmerFor(ShimaDef shima, int n) {
		m_networkNumberForShima[shima.i] = n;
	}

}
